package fr.ul.ade.secunde.ui;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;

import com.actionbarsherlock.app.SherlockFragment;

import fr.ul.ade.R;
import fr.ul.ade.secunde.database.AdeWebContract;
import fr.ul.ade.secunde.util.ResourceManager;
import fr.ul.ade.secunde.util.ScheduleConstants;
import fr.ul.ade.secunde.util.UiUtils;

/**
 * Fragment affichant le formulaire de création d'un emploi du temps.
 * 
 * @author Michaël Schnell
 * @since Jul 2, 2013
 * 
 * @version 1.6
 */
public class CreateScheduleFragment
		extends SherlockFragment
		implements LoaderCallbacks<Cursor>, OnClickListener {

	private static final String EMPTY_STRING = ""; //$NON-NLS-1$

	private static final int REMOVE_RESOURCE_ICON = 1;

	private CursorAdapter projectsAdapter;

	public static CreateScheduleFragment newInstance() {
		return new CreateScheduleFragment();
	}

	// --------------------------------------------------------
	//
	// Création
	//
	// --------------------------------------------------------

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		final View view = inflater.inflate(R.layout.fragment_create_schedule,
				container,
				false);

		final ImageView addResource = (ImageView) view
				.findViewById(R.id.create_schedule_add_remove_resource_icon);
		addResource.setOnClickListener(new AddResourceListener(this));

		final Button save = (Button) view
				.findViewById(R.id.create_schedule_save);
		save.setOnClickListener(this.getSaveListener());

		final Button cancel = (Button) view
				.findViewById(R.id.create_schedule_cancel);
		cancel.setOnClickListener(this);

		return view;
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		final View view = this.getView();

		final Spinner periods = (Spinner) view
				.findViewById(R.id.create_schedule_period_spinner);
		periods.setAdapter(new PeriodAdapter(this.getActivity(),
				R.array.create_schedule_period_entries,
				R.array.create_schedule_period_values));

		final Spinner projets = (Spinner) view
				.findViewById(R.id.create_schedule_project_spinner);
		this.projectsAdapter = new ProjectsAdapter(this.getActivity());
		projets.setAdapter(this.projectsAdapter);

		this.getLoaderManager().initLoader(ProjectQuery._TOKEN, null, this);
	}

	/**
	 * Construit et retourne l'écouteur pour le bouton d'enregistrement.
	 * 
	 * @return {@link OnClickListener} pour le bouton d'enregistrement
	 */
	protected OnClickListener getSaveListener() {
		return new CreateScheduleListener(this);
	}

	// --------------------------------------------------------
	//
	// Traitement des données
	//
	// --------------------------------------------------------

	@Override
	public Loader<Cursor> onCreateLoader(int id, Bundle args) {
		Uri projectUri = AdeWebContract.Project.CONTENT_URI;
		String[] projection = ProjectQuery.PROJECTION;
		String selection = null;
		String[] selectionArgs = null;

		return new CursorLoader(this.getActivity(), projectUri, projection,
				selection, selectionArgs, AdeWebContract.Project.DEFAULT_SORT);
	}

	@Override
	public void onLoaderReset(Loader<Cursor> loader) {
		/* Nothing to be done. */
	}

	@Override
	public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
		if (this.getActivity() == null) {
			return;
		}// if

		this.projectsAdapter.changeCursor(data);
	}

	// --------------------------------------------------------
	//
	// Traitement des évènements
	//
	// --------------------------------------------------------

	@Override
	public void onClick(View v) {
		this.getActivity().onBackPressed();
	}

	// --------------------------------------------------------
	//
	// Données chargées
	//
	// --------------------------------------------------------

	protected interface ProjectQuery {
		final int _TOKEN = 0x1;

		final String[] PROJECTION = { BaseColumns._ID,
				AdeWebContract.Project.PROJECT_NAME,
				AdeWebContract.Project.PROJECT_ADEWEB_ID,
				AdeWebContract.Project.PROJECT_LOADED };

		final int _ID = 0;
		final int PROJECT_NAME = 1;
		final int PROJECT_ADEWEB_ID = 2;
		final int PROJECT_LOADED = 3;
	}

	// --------------------------------------------------------
	//
	// Gestion des ressources
	//
	// --------------------------------------------------------

	protected static class AddResourceListener
			implements OnClickListener {

		protected CreateScheduleFragment fragment;

		public AddResourceListener(CreateScheduleFragment fragment) {
			this.fragment = fragment;
		}

		@Override
		public void onClick(View v) {
			this.fragment.addResource();
		}
	}

	protected static class RemoveResourceListener
			implements OnClickListener {

		protected CreateScheduleFragment fragment;

		public RemoveResourceListener(CreateScheduleFragment fragment) {
			this.fragment = fragment;
		}

		@Override
		public void onClick(View v) {
			this.fragment.removeResource((View) v.getParent());
		}
	}

	protected void addResource() {
		final View fragmentView = this.getView();
		this.addResource(UiUtils.getTextViewContent(fragmentView,
				R.id.create_schedule_resource));
	}

	protected void addResource(String resource) {
		final LayoutInflater inflater = this.getActivity().getLayoutInflater();
		final View fragmentView = this.getView();

		final LinearLayout resourceContainer = (LinearLayout) fragmentView
				.findViewById(R.id.create_schedule_resources_container);

		final View newResource = inflater
				.inflate(R.layout.fragment_create_schedule_resource,
						resourceContainer,
						false);

		final ImageView icon = (ImageView) newResource
				.findViewById(R.id.create_schedule_add_remove_resource_icon);
		icon.setImageLevel(REMOVE_RESOURCE_ICON);
		icon.setOnClickListener(new RemoveResourceListener(this));

		UiUtils.setTextViewContent(newResource,
				R.id.create_schedule_resource,
				resource);

		UiUtils.setTextViewContent(fragmentView,
				R.id.create_schedule_resource,
				EMPTY_STRING);

		resourceContainer.addView(newResource);
	}

	protected void removeResource(View resourceView) {
		final LinearLayout resourceContainer = (LinearLayout) this.getView()
				.findViewById(R.id.create_schedule_resources_container);
		resourceContainer.removeView(resourceView);
	}

	// --------------------------------------------------------
	//
	// Adaptateur pour la liste des périodes
	//
	// --------------------------------------------------------

	protected static class PeriodAdapter
			extends BaseAdapter
			implements ExtendedSpinnerAdapter<String> {

		private Context mContext;
		private CharSequence[] texts;
		private CharSequence[] values;

		public PeriodAdapter(Context context, int resTextId, int resValId) {
			this.mContext = context;
			this.texts = context.getResources().getTextArray(resTextId);
			this.values = context.getResources().getTextArray(resValId);
		}

		@Override
		public int getCount() {
			return this.texts.length;
		}

		@Override
		public String getItem(int position) {
			return this.values[position].toString();
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			if (convertView == null) {
				final LayoutInflater inflater = (LayoutInflater) this.mContext
						.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
				convertView = inflater
						.inflate(android.R.layout.simple_spinner_item,
								parent,
								false);
			}

			final TextView text = (TextView) convertView
					.findViewById(android.R.id.text1);
			text.setText(this.texts[position]);

			return convertView;
		}

		/** {@inheritDoc} */
		@Override
		public int getItemPosition(String item) {
			int position = -1;
			int current = 0, count = this.getCount();
			while (position == -1 && current < count) {
				if (item.equals(this.values[current].toString())) {
					position = current;
				}
				current++;
			}
			return position;
		}

	}

	// --------------------------------------------------------
	//
	// Adaptateur pour la liste des projets
	//
	// --------------------------------------------------------

	protected static class ProjectsAdapter
			extends CursorAdapter
			implements ExtendedSpinnerAdapter<Long> {

		public ProjectsAdapter(Context context) {
			super(context, null, 0 /* no flags set. */);
		}

		@Override
		public View newView(Context context, Cursor cursor, ViewGroup parent) {
			LayoutInflater inflater = (LayoutInflater) context
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			return inflater.inflate(android.R.layout.simple_spinner_item,
					parent,
					false);
		}

		@Override
		public void bindView(View view, Context context, Cursor cursor) {
			final TextView text = (TextView) view
					.findViewById(android.R.id.text1);
			text.setText(cursor.getString(ProjectQuery.PROJECT_NAME));
		}

		@Override
		public int getItemPosition(Long item) {
			Log.d("[getItemPosition]",
					"---------------------------------------");
			int position = -1;
			int current = 0, count = this.getCount();
			long projectId = item.longValue();
			Log.d("[getItemPosition]", "Count: " + count);
			while (/* position == -1 && */current < count) {
				final Cursor data = (Cursor) this.getItem(current);
				Log.d("[P]", "Id: " + data.getLong(ProjectQuery._ID) + " =?= "
						+ projectId + "(" + item + ") pos: " + position);
				if (projectId == data.getLong(ProjectQuery._ID)) {
					position = current;
				}
				current++;
			}
			return position;
		}

	}// class ProjectsAdapter

	// --------------------------------------------------------
	//
	// Création de l'emploi du temps
	//
	// --------------------------------------------------------

	protected ScheduleValues retrieveScheduleValues() {
		final ScheduleValues schedule = new ScheduleValues();
		final View form = this.getView();
		final LinearLayout resourceContainer = (LinearLayout) form
				.findViewById(R.id.create_schedule_resources_container);

		schedule.name = UiUtils.getTextViewContent(form,
				R.id.create_schedule_name);

		schedule.description = UiUtils.getTextViewContent(form,
				R.id.create_schedule_description);

		schedule.appendResource(UiUtils.getTextViewContent(form,
				R.id.create_schedule_resource));
		int nResources = resourceContainer.getChildCount();
		for (int res = 0; res < nResources; ++res) {
			final String resValue = UiUtils
					.getTextViewContent(resourceContainer.getChildAt(res),
							R.id.create_schedule_resource);
			schedule.appendResource(resValue);
		}
		schedule.formatResources();

		final Spinner periods = (Spinner) form
				.findViewById(R.id.create_schedule_period_spinner);
		schedule.period = Integer.valueOf((String) periods.getSelectedItem());

		final Spinner projects = (Spinner) form
				.findViewById(R.id.create_schedule_project_spinner);
		schedule.projectId = Long.valueOf(((Cursor) projects.getSelectedItem())
				.getLong(ProjectQuery._ID));

		return schedule;
	}

	/**
	 * Ecouteur qui lance la création d'un emploi du temps à partir des valeurs
	 * recupérées dans le formulaire.
	 * 
	 * @author Michaël Schnell
	 * @since Jul 8, 2013
	 * 
	 * @version 1.3
	 */
	private static class CreateScheduleListener
			implements OnClickListener {

		private CreateScheduleFragment fragment;

		public CreateScheduleListener(CreateScheduleFragment fragment) {
			this.fragment = fragment;
		}

		@Override
		public void onClick(View v) {
			ScheduleValues scheduleValues = this.fragment
					.retrieveScheduleValues();
			ContentValues values = scheduleValues.getContent();

			final FragmentActivity activity = this.fragment.getActivity();
			activity.getContentResolver()
					.insert(AdeWebContract.Schedule.CONTENT_URI, values);
			activity.onBackPressed();
		}

	}// class CreateScheduleListener

	/**
	 * Conteneur regroupant les valeurs nécessaire à la création d'un emploi du
	 * temps.
	 * 
	 * @author Michaël Schnell
	 * @since Jul 11, 2013
	 * 
	 * @version 1.3
	 */
	protected static class ScheduleValues {
		String name;
		String description;
		StringBuilder resources;
		StringBuilder studentNumbers;
		Integer period;
		Long projectId;

		public ScheduleValues() {
			this.resources = new StringBuilder();
			this.studentNumbers = new StringBuilder();
		}

		/**
		 * Ajoute une ressource à ce conteneur. Vérifie si la ressource est
		 * valide avant de l'ajouter dans la liste des ressources ou la liste
		 * des numéros étudiants.
		 * 
		 * @param schedule
		 * @param resource
		 */
		public void appendResource(final String resource) {
			if (TextUtils.isEmpty(resource)) {
				return;
			}

			if (ResourceManager.isStudentNumber(resource)) {
				this.studentNumbers.append(ResourceManager.RESOURCE_SEPARATOR);
				this.studentNumbers.append(resource);
			} else {
				this.resources.append(ResourceManager.RESOURCE_SEPARATOR);
				this.resources.append(resource);
			}
		}

		/**
		 * Formatte les ressources pour l'insertion dans la base de données.
		 */
		public void formatResources() {
			if (this.resources.length() > 0) {
				this.resources.delete(0,
						ResourceManager.RESOURCE_SEPARATOR.length());
			}
			if (this.studentNumbers.length() > 0) {
				this.studentNumbers.delete(0,
						ResourceManager.RESOURCE_SEPARATOR.length());
			}
		}

		/**
		 * Crée un {@link ContentValues} initialisé avec les valeurs de ce
		 * conteneur pour la création d'un emploi du temps.
		 * 
		 * @return instance de {@link ContentValues} initialisée avec les
		 *         valeurs de ce conteneur
		 */
		public ContentValues getContent() {
			ContentValues values = new ContentValues();
			values.put(AdeWebContract.Schedule.SCHEDULE_NAME, this.name);
			values.put(AdeWebContract.Schedule.SCHEDULE_DESCRIPTION,
					this.description);
			values.put(AdeWebContract.Schedule.SCHEDULE_PERIOD, this.period);
			values.put(AdeWebContract.Schedule.SCHEDULE_PROJECT, this.projectId);
			values.put(AdeWebContract.Schedule.SCHEDULE_RESOURCES,
					this.resources.toString());
			values.put(AdeWebContract.Schedule.SCHEDULE_STUDENT_NUM,
					this.studentNumbers.toString());

			values.put(AdeWebContract.Schedule.SCHEDULE_STATE,
					ScheduleConstants.DEFAULT_SCHEDULE_STATE);
			values.put(AdeWebContract.Schedule.SCHEDULE_NOTIFY_UPDATE,
					ScheduleConstants.DEFAULT_SCHEDULE_NOTIFY_UPDATE);

			return values;
		}

	}// class ScheduleValues

}// class CreateScheduleFragment